package jaux.tank.api.controller.system;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.List;
import java.util.Optional;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import io.swagger.annotations.ApiOperation;
import jaux.tank.admin.config.properties.TankProperties;
import jaux.tank.bean.core.ShiroUser;
import jaux.tank.bean.entity.system.Attachment;
import jaux.tank.bean.entity.system.AttachmentBase;
import jaux.tank.bean.enumeration.AttachmentSaveTo;
import jaux.tank.bean.vo.front.Ret;
import jaux.tank.bean.vo.front.Rets;
import jaux.tank.service.system.AttachmentService;
import jaux.tank.shiro.ShiroKit;
import jaux.tank.utils.DateUtil;
import jaux.tank.utils.HttpKit;

/**
 * 工作附件统一管理接口
 * 管理工作附件的上传、查询、查看等。
 * @author dong
 *
 */
@RestController("attachmentApiController")
@RequestMapping("/api/attachment")
public class AttachmentController {
	
	
	 private static final String SAVE_TO_DB = "db";
	private static final String SAVE_TO_FILESYSTEM = "filesystem";
	private static final String FILE_TOO_LARGE_TO_DB_MSG_FORMAT = "保存到数据库的文件大小（%d）超出限制（%d）";
	private static final String FILE_SAVE_FAILURE_MSG = "保存附件文件失败";
	private static final String SAVE_TO_S3_UNABLE = "保存到S3不可用";
	private static final Logger logger = LoggerFactory.getLogger(AttachmentController.class);
	@Autowired
	private AttachmentService attachmentService;
	
	@Autowired
	TankProperties tankProps;

    /**
     * 查询所用工作附件（不包括附件内容）
     * @return
     */
    @GetMapping
    @ApiOperation("查询所用工作附件（不包括附件内容）")
    public Ret<List<AttachmentBase>> listAttachments() {
		return Rets.success(attachmentService.listAttachments());
    }

    /**
     * 获取附件内容
     * @param attachmentId 附件主键id
     * @return
     */
    @GetMapping("/download")
    @ApiOperation("获取附件内容")
    public ResponseEntity<Resource> serveFile(@RequestParam String attachmentId) {

    	AttachmentBase attachment  = attachmentService.getAttachmentBaseById(attachmentId);
    	 Resource resource = null;
		try {
			resource = attachmentService.getAttachmentResource(attachmentId);
		} catch (IOException e) {
			logger.error("获取附件资源失败", e);
		}
    	if (resource != null && resource.exists() && resource.isReadable()) {
    		String fileName= "file";
    		try {
				fileName = URLEncoder.encode(attachment.getFileName(),"UTF-8");
			} catch (UnsupportedEncodingException e) {
				logger.warn("附件文件名转码失败", e);
			}
    		return ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION,
                "attachment; filename=\"" + fileName + "\"").body(resource);
    	}
    	return null;
    }
    /**
     * 上传工作附件
     * @param file 上传文件对象
     * @param saveTo 保存方式：filesystem、db
     * @param description 附件描述
     * @return
     */
    @PostMapping("/upload")
    @ApiOperation("上传工作附件")
    public Ret<Attachment> handleFileUpload(@RequestParam("file") MultipartFile file,
    		@RequestParam(name="saveTo",defaultValue=SAVE_TO_FILESYSTEM ) String saveTo,
    		@RequestParam(name="description",required=false) String description) {
    	long fileSize = file.getSize();
    	if(AttachmentSaveTo.DB.name().equalsIgnoreCase(saveTo)
    			&& file.getSize() > tankProps.getWorkAttachmentLimitToDb()) {
    		String fileTooLargeForDbMessage = String.format(FILE_TOO_LARGE_TO_DB_MSG_FORMAT,fileSize,tankProps.getWorkAttachmentLimitToDb());
			logger.error(FILE_SAVE_FAILURE_MSG, new RuntimeException(fileTooLargeForDbMessage));
			return  Rets.failure(FILE_SAVE_FAILURE_MSG+":"+fileTooLargeForDbMessage);
    	}
    	if(AttachmentSaveTo.S3.name().equalsIgnoreCase(saveTo)
    			&&  !tankProps.isWorkAttachmentSaveToS3()) {
    		logger.error(FILE_SAVE_FAILURE_MSG, new RuntimeException(SAVE_TO_S3_UNABLE));
    		return  Rets.failure(FILE_SAVE_FAILURE_MSG+":"+SAVE_TO_S3_UNABLE);
    	}
    	Attachment attachment = new Attachment();
    	attachment.setSaveTo(saveTo);
    	attachment.setDescription(description);
		attachment.setFileName(file.getOriginalFilename());
    	attachment.setFileType(file.getContentType());
    	attachment.setFileSize(file.getSize());
    	attachment.setUploadTime(DateUtil.getTime());
		Optional<ShiroUser> uploader = ShiroKit.getApiRequestUser();
		if(uploader.isPresent()) {
			attachment.setUploaderId(uploader.get().getId());
		}
		try   {
			File tempAttachmentFile = File.createTempFile("uploadattachment", "");
			tempAttachmentFile.deleteOnExit();
			file.transferTo(tempAttachmentFile); // save attachment to temporary file
			Attachment savedAttachment = attachmentService.save(attachment, tempAttachmentFile);
			return Rets.success(savedAttachment);
		} catch (IOException e) {
			logger.error(FILE_SAVE_FAILURE_MSG, e);
			return Rets.failure(FILE_SAVE_FAILURE_MSG+":"+e.getMessage());
		}
    }
    /**
     * 修改工作附件
     * @param file 上传文件对象
     * @param saveTo 保存方式：filesystem、db
     * @param description 附件描述
     * @return
     */
    @PostMapping("/update")
    @ApiOperation("修改工作附件")
    public Ret<Attachment> updateAttachment(@RequestParam(required = false,name="file") MultipartFile file,
    		@RequestParam(name="saveTo",defaultValue=SAVE_TO_FILESYSTEM) String saveTo,
    		@RequestParam(name="description",required=false) String description,
    		@RequestParam(name="id") String id) {
    	boolean changeContent = file !=null;
    	long fileSize = 0L;
    	if(changeContent) {
			fileSize = file.getSize();
		}else {
			AttachmentBase oldAttachment = attachmentService.getAttachmentBaseById(id);
			if(oldAttachment != null) {
				fileSize = oldAttachment.getFileSize();
			}
		}
    	if(AttachmentSaveTo.DB.name().equalsIgnoreCase(saveTo)
    			&& fileSize > tankProps.getWorkAttachmentLimitToDb()) {
    		String fileTooLargeForDbMessage = String.format(FILE_TOO_LARGE_TO_DB_MSG_FORMAT,fileSize,tankProps.getWorkAttachmentLimitToDb());
			logger.error(FILE_SAVE_FAILURE_MSG, new RuntimeException(fileTooLargeForDbMessage));
			return  Rets.failure(FILE_SAVE_FAILURE_MSG+":"+fileTooLargeForDbMessage);
    	}
    	if(AttachmentSaveTo.S3.name().equalsIgnoreCase(saveTo)
    			&&  !tankProps.isWorkAttachmentSaveToS3()) {
    		logger.error(FILE_SAVE_FAILURE_MSG, new RuntimeException(SAVE_TO_S3_UNABLE));
    		return  Rets.failure(FILE_SAVE_FAILURE_MSG+":"+SAVE_TO_S3_UNABLE);
    	}
    	Attachment attachment = new Attachment();
    	attachment.setId(id);
    	attachment.setSaveTo(saveTo);
    	attachment.setDescription(description);
    	attachment.setUploadTime(DateUtil.getTime());
    	Optional<ShiroUser> uploader = ShiroKit.getApiRequestUser();
		if(uploader.isPresent()) {
			attachment.setUploaderId(uploader.get().getId());
		}
    	try{
    		File tempAttachmentFile = null;
    		if(file !=null) {
    			tempAttachmentFile = File.createTempFile("uploadattachment", "");
    			tempAttachmentFile.deleteOnExit();
    			file.transferTo(tempAttachmentFile); // save attachment to temporary file
    		}
			Attachment savedAttachment = attachmentService.updateAttachment(attachment, tempAttachmentFile);
			return Rets.success(savedAttachment);
		} catch (IOException e) {
			logger.error(FILE_SAVE_FAILURE_MSG, e);
			return Rets.failure(FILE_SAVE_FAILURE_MSG+":"+e.getMessage());
		}
    }

    /**
     * 根据附件主键 删除文件
     * @param attachmentId
     * @return
     */
    @DeleteMapping("/remove")
    @ApiOperation("根据附件主键删除文件")
    public Ret<String> deleteAttachment(@RequestParam("attachmentId") String attachmentId){
    	Ret<String> result= Rets.success(attachmentId);
		try {
			attachmentService.deleteAttachment(attachmentId);
		} catch (Exception e) {
			logger.warn("删除附件文件失败",e);
			result = Rets.failure("删除附件文件失败");
		}
    	
    	return result;
    }
    
}
